package com.benshou.bsoa.common.quartz;

import lombok.NonNull;
import org.quartz.*;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;

@Component
public class QuartzJobProcessor implements BeanPostProcessor, ApplicationContextAware {

    private ApplicationContext applicationContext;

    @NonNull
    @Override
    public Object postProcessAfterInitialization(@NonNull Object bean, @NonNull String beanName) throws BeansException {
        Class<?> targetClass = AopUtils.getTargetClass(bean);
        for (Method method : targetClass.getDeclaredMethods()) {
            if (method.isAnnotationPresent(QuartzJob.class)) {
                QuartzJob annotation = method.getAnnotation(QuartzJob.class);
                addJob(bean, method, annotation);
            }
        }
        return bean;
    }

    private void addJob(Object bean, Method method, QuartzJob annotation) {
        try {
            // 获取调度器实例
            Scheduler scheduler = applicationContext.getBean(Scheduler.class);

            // 检查任务是否已经存在
            JobKey jobKey = JobKey.jobKey(method.getName(), bean.getClass().getName());
            if (scheduler.checkExists(jobKey)) {
                return; // 如果任务已存在，则跳过初始化
            }

            JobDetail jobDetail = JobBuilder.newJob(QuartzJobWrapper.class)
                    .withIdentity(method.getName(), bean.getClass().getName())
                    .usingJobData("beanName", bean.getClass().getName())
                    .usingJobData("methodName", method.getName())
                    .storeDurably()
                    .build();

            Trigger trigger;
            if (annotation.fixedRate() > 0) {
                trigger = TriggerBuilder.newTrigger()
                        .withIdentity(method.getName() + "Trigger", bean.getClass().getName())
                        .withSchedule(
                                SimpleScheduleBuilder.simpleSchedule()
                                        .withIntervalInMilliseconds(
                                                annotation.fixedRate())
                                        .repeatForever()
                        ).build();
            }else if (StringUtils.hasLength(annotation.fixedRateString())) {
                trigger = TriggerBuilder.newTrigger()
                        .withIdentity(method.getName() + "Trigger", bean.getClass().getName())
                        .withSchedule(
                                SimpleScheduleBuilder.simpleSchedule()
                                        .withIntervalInMilliseconds(
                                                Long.parseLong(resolveCronExpression(annotation.fixedRateString())))
                                        .repeatForever()
                        ).build();
            } else if (!annotation.cron().isEmpty()) {
                trigger = TriggerBuilder.newTrigger()
                        .withIdentity(method.getName() + "Trigger", bean.getClass().getName())
                        .withSchedule(CronScheduleBuilder.cronSchedule(resolveCronExpression(annotation.cron())))
                        .build();
            } else {
                throw new IllegalArgumentException("必须指定 fixedRate 或 cron 参数");
            }

            scheduler.scheduleJob(jobDetail, trigger);
        } catch (Exception e) {
            throw new RuntimeException("调度任务失败", e);
        }
    }

    @Override
    public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    private String resolveCronExpression(String expression) {
        Environment environment = this.applicationContext.getEnvironment();
        return environment.resolvePlaceholders(expression);
    }
}